package services

import (
	"errors"
	"fmt"
	"strings"
	"text/tabwriter"

	"github.com/deploy-services/v2/filesystem"

	driver_handle "github.com/deploy-services/v2/driver"
	"github.com/deploy-services/v2/kcfg"
)

type driver struct {
	name string
	port int64
	args string
}

func (d *driver) dump(prefix string) string {
	var ss string
	ss += fmt.Sprintf("%vdriver\n", prefix)
	ss += fmt.Sprintf("%v\tname:%v\n", prefix, d.name)
	ss += fmt.Sprintf("%v\tport:%v\n", prefix, d.port)
	ss += fmt.Sprintf("%v\targs:%v\n", prefix, d.args)
	return ss
}

func parseDriver(n *kcfg.Node) driver {
	d := driver{}
	d.name = n.Child("name").GetString()
	d.port = n.Child("port").GetInt()
	d.args = n.Child("args").GetString()
	return d
}

type node struct {
	name      string
	system    string
	base      string
	host      string
	dv        driver
	dv_handle driver_handle.Driver
	n         *kcfg.Node
	enable    bool
}

func (n *node) generate_workdir(service string, path string) string {
	return "cd " + filesystem.GetRemotePath(n.base, service, path)
}

func (n *node) generate_env(service string, env env_type) (string, error) {
	if n.system != "linux" {
		return "", errors.New("Not Support System:" + n.system)
	}

	switch env.type_ {
	case "system":
		value := filesystem.GetRemotePath(n.base, service, env.value)
		return fmt.Sprintf("%v=\"%v:$%v\"", env.name, value, env.name), nil
	case "normal":
		return fmt.Sprintf("%v=\"%v\"", env.name, env.value), nil
	case "path":
		value := filesystem.GetRemotePath(n.base, service, env.value)
		return fmt.Sprintf("%v=\"%v\"", env.name, value), nil
	}

	return "", errors.New("Not Support Type:" + env.type_)
}

func (s *node) enable_node(state bool) {
	s.enable = state
}

func (s *node) run_cmd(cmd command) (*node, string, error) {
	if !s.enable {
		return s, "Disabled", nil
	}
	handle := s.dv_handle.CreateHandle()
	if handle == nil {
		return s, "", errors.New("Connect " + s.host + " failed")
	}
	defer handle.Close()

	ret, err := handle.Execute(cmd.cmd)
	return s, ret, err
}

func (n *node) generate_runcmd(service string, workdir string, envs []env_type, cmd string) (string, error) {
	if n.system != "linux" {
		return cmd, errors.New("Not Support System:" + n.system)
	}

	var cmd_string string
	if envs != nil && len(envs) > 0 {
		// 先生成环境变量
		var env_string string
		for _, env := range envs {
			str, _ := n.generate_env(service, env)
			if len(str) == 0 {
				continue
			}

			if len(env_string) == 0 {
				env_string = fmt.Sprintln(str)
			} else {
				env_string += fmt.Sprintln(str)
			}
		}

		if len(env_string) > 0 {
			cmd_string = env_string
		}
	}

	if len(workdir) > 0 {
		str := n.generate_workdir(service, workdir)
		if len(cmd_string) > 0 {
			cmd_string += fmt.Sprintln(str)
		} else {
			cmd_string = fmt.Sprintln(str)
		}
	}

	if len(cmd_string) > 0 {
		cmd_string += fmt.Sprintln(cmd)
	} else {
		cmd_string = cmd
	}

	return cmd_string, nil
}

// func (n *node) dump(prefix string) string {
// 	var ss string
// 	// ss += fmt.Sprintf("%v%v\n", n.name)
// 	ss += fmt.Sprintf("%vsystem:%v\n", prefix, n.system)
// 	ss += fmt.Sprintf("%vbase:%v\n", prefix, n.base)
// 	ss += fmt.Sprintf("%vhost:%v\n", prefix, n.host)
// 	ss += n.dv.dump(prefix)
// 	return ss
// }

type nodes []*node

func (s *nodes) run_cmd(cmd command) (rnodes nodes, rests []string, errs []error) {
	for _, v := range *s {
		ns, rs, es := v.run_cmd(cmd)
		rnodes = append(rnodes, ns)
		rests = append(rests, rs)
		errs = append(errs, es)
	}

	return rnodes, rests, errs
}

func (s *nodes) enable(host, base string, state bool) {
	for _, v := range *s {
		if (len(host) > 0 && v.host == host) || len(host) == 0 {
			if len(base) == 0 || (len(base) > 0 && v.base == base) {
				v.enable_node(state)
			}
		}
	}
}

type mnodes map[string]nodes

func (nn *mnodes) dump() string {
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Name\tHost\tBase\tSystem\tEnable")
	fmt.Fprintln(w, "----\t----\t----\t------\t------")
	for _, ns := range *nn {
		for _, n := range ns {
			fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\n", n.name, n.host, n.base, n.system, n.enable)
		}
	}
	w.Flush()
	return bs.String()
}

func (s *mnodes) run_cmd(node string, cmd command) (rnodes nodes, rests []string, errs []error) {
	for k, v := range *s {
		if len(node) == 0 || k == node {
			ns, rs, es := v.run_cmd(cmd)
			rnodes = append(rnodes, ns...)
			rests = append(rests, rs...)
			errs = append(errs, es...)
		}
	}

	return rnodes, rests, errs
}

func (s *mnodes) enable(node_str string, state bool) {
	// 先要解析 node_str 格式
	// return n.name + "@" + n.host + ":" + n.base
	var name, host, base string
	pos := strings.Index(node_str, "@")
	if pos > 0 {
		name = node_str[0:pos]
		node_str = node_str[pos+1:]
		pos = strings.Index(node_str, ":")
		if pos > 0 {
			host = node_str[0:pos]
			base = node_str[pos+1:]
		} else {
			host = node_str
		}
	} else {
		name = node_str
	}

	for k, v := range *s {
		if (len(name) > 0 && k == name) || len(name) == 0 {
			v.enable(host, base, state)
		}
	}
}

func parseMNodes(n []*kcfg.Node) mnodes {
	rest := make(mnodes)
	for _, v := range n {
		names := v.Childs("name")
		names_set := NewSet()
		for _, name_node := range names {
			name := name_node.GetString()
			if names_set.Has(name) {
				continue
			}
			names_set.Insert(name)
			nd := &node{
				n:      v,
				enable: true,
			}
			nd.name = name
			nd.system = v.Child("system").GetString()
			nd.base = v.Child("base").GetString()
			nd.host = v.Child("host").GetString()
			nd.dv = parseDriver(v.Child("driver"))
			nd.dv_handle = driver_handle.Create(nd.dv.name, nd.host, int(nd.dv.port), nd.dv.args)
			var ln nodes
			var ok bool
			if ln, ok = rest[nd.name]; !ok {
				ln = make(nodes, 0)
			}
			ln = append(ln, nd)
			rest[nd.name] = ln
		}
	}
	return rest
}
